package com.infoclue.modules.km.utils;


import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.jeecg.modules.KM.common.config.BaseConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/***
 * 封装与weaver集成
 */
@Service
@Slf4j
public class WeaverService {

    @Autowired
    private BaseConfig baseConfig;

    @Value(value = "${jeecg.minio.minio_url}")
    private String minioUrl;

    @Value(value = "${jeecg.minio.minio_name}")
    private String accessKey;

    @Value(value = "${jeecg.minio.minio_pass}")
    private String secretKey;

    @Value("${jeecg.minio.avatar}")
    private String avatarBucket;

    @Value("${jeecg.minio.kmsfile}")
    private String kmsfile;

    @Value("${jeecg.minio.minio_picture}")
    private String minioPicture;

    public static void main(String[] args) throws Exception {
        // 设置你的MinIO服务地址、端口、Access Key和Secret Key
        String endpoint = "10.0.0.121";
        int port = 31001;
        String accessKey = "miniominio";
        String secretKey = "miniominio";
        String bucketName = "fortify";
        String objectKey = "20240604102535/pom.xml";
        MinioClient minioClient = MinioClient.builder()
                .endpoint(endpoint, port, false)
                .credentials(accessKey, secretKey)
                .build();

        GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder()
                .bucket(bucketName)
                .object(objectKey)
                .method(Method.GET)
                .build();


        // 生成预签名的URL，过期时间设置为1小时
        String url = minioClient.getPresignedObjectUrl(objectUrlArgs);

        System.out.println("Presigned URL for PUT operation: " + url);
    }

    public Boolean bucketExists(String buckectName){
        // 检查桶是否存在
        boolean found = false;
        try{
            found = getMiniClient().bucketExists(BucketExistsArgs.builder().bucket(buckectName).build());
        } catch (MinioException e) {
            System.err.println("Error MinioException: " + e);
        } catch (Exception e) {
            System.err.println("Exception: " + e);
        }

        if (!found) {
            System.out.println("桶不存在，请检查桶名是否正确");
            return false;
        }
        return true;
    }

    /***
     * 移动对象
     * @param bucketName
     * @param objectName
     * @param destBucketName
     * @param destObjectName
     * @return
     */
    public Boolean moveObject(String bucketName, String objectName,String destBucketName, String destObjectName){
        // 检查桶是否存在
        boolean success = false;
        try{
            log.info("----------moveObject--------------"+destBucketName+"-----"+destObjectName);
            // 创建CopySource对象
            CopySource copySource = CopySource.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();


//            // 可以设置额外的复制选项，例如修改元数据等
//            CopyConditions copyConditions = null; // 默认不设置任何条件

            // 构建CopyObjectArgs实例
            CopyObjectArgs.Builder builder = CopyObjectArgs.builder()
                    .source(copySource)
                    .bucket(destBucketName)
                    .object(destObjectName);

            CopyObjectArgs copyObjectArgs = builder.build();

            ObjectWriteResponse writeResponse=getMiniClient().copyObject(copyObjectArgs);


            log.info("----------writeResponse--------------"+writeResponse.toString());
            RemoveObjectArgs removeObjectArgs= RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build();
            getMiniClient().removeObject(removeObjectArgs);
            success=true;
        } catch (MinioException e) {
            log.error("Error MinioException---------------------" + e);
        } catch (Exception e) {
            log.error("Exception---------------------" + e);
        }


        return success;
    }



    public MinioClient getMiniClient(){
        return MinioClient.builder()
                .endpoint(minioUrl)
                .credentials(accessKey, secretKey)
                .build();
    }

    public String getMinioUrl(String bucket, String filePath) {
        GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder()
            .bucket(bucket)
            .object(filePath)
            .expiry(60*60)
            .method(Method.GET)
            .build();

        String minioUrl = "";
        // 生成预签名的URL，过期时间设置为1小时
        try {
            minioUrl = getMiniClient().getPresignedObjectUrl(objectUrlArgs);
            System.out.println("Minio 预签名URL: " + minioUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return minioUrl;
    }

    /***
     * 获取特定桶/文件夹下的文件
     * @param bucketName
     * @param folder
     * @return
     */
    public List<Result<Item>>   getFileObjects(String bucketName, String folder) {

        String prefix = folder+"/";

        ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(true).build();

        List<Result<Item>> results = new ArrayList<>();

        log.info("bucketName----------------"+bucketName);

        log.info("prefix----------------"+prefix);
        try {
            getMiniClient().listObjects(listObjectsArgs).forEach(results::add);
            // Process the results.
            for (Result<Item> result : results) {
                Item item = result.get();
                log.info(item.objectName());
            }
        }catch (Exception ex){
            log.error("ex----------------"+ex.getMessage());
        }

        return results;
    }

    /**
     * 从MinIO获取文件的输入流。
     *
     * @param objectName MinIO中的文件名
     * @return 文件的输入流
     * @throws IOException 如果流创建失败或读取时发生I/O错误
     * @throws MinioException 如果MinIO操作失败
     */
    public InputStream getObjectInputStream(String objectName) throws IOException, MinioException {
        log.info("-----------objectName-----------"+objectName);
        // 使用MinIO客户端的getObject方法获取文件的输入流
        InputStream inputStream = null;
        try {
            inputStream = getMiniClient().getObject(
                    GetObjectArgs.builder().bucket(kmsfile).object(objectName).build()
            );
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        log.info("-----------inputStream-----------"+inputStream);
        return inputStream;
    }

    /***
     * 下载并获取MINIO 文件
     * @param uploadDir
     * @param objectName
     * @return
     */
    public File getMinioFile(String uploadDir,String objectName) {

        log.info("-----bucket-----------"+kmsfile);
        log.info("-----objectName-----------"+objectName);
        String localobjectName=objectName.replace("/","");
        log.info("-----localobjectName-----------"+localobjectName);
        String localFilePath = uploadDir+"/"+localobjectName;
        log.info("localFilePath-------------------"+localFilePath);
        File localFile =null;

        try (InputStream stream =this.downloadFile(kmsfile,objectName)) {
            // 创建本地文件
            localFile = new File(localFilePath);
            try (FileOutputStream out = new FileOutputStream(localFile)) {
                // 将下载的文件内容写入本地文件
                byte[] buf = new byte[1024];
                int bytesRead;
                while ((bytesRead = stream.read(buf)) > 0) {
                    out.write(buf, 0, bytesRead);
                }
            }
        }
        catch(Exception ex){
            log.error("EXCEPTION-------"+ex.getMessage());
        }
        return localFile;
    }

    public String getKmsfile() {
        return kmsfile;
    }

    public String getAvatar() {
        return avatarBucket;
    }

    //    创建桶（createBucket）
    public void createBucket(String bucketName) throws Exception {
        boolean exists = bucketExists(bucketName);
        if (!exists) {
            getMiniClient().makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    //    上传文件（uploadFile）
//    public void uploadFile(String bucketName, String objectName, InputStream stream, String contentType) throws Exception {
//        minioClient.putObject(
//                PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
//                        stream, -1, 10485760)
//                        .contentType(contentType)
//                        .build());
//    }

    //        下载文件（downloadFile）
    public InputStream downloadFile(String bucketName, String objectName) throws Exception {
        return getMiniClient().getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /***
     * 下载为MultiPartFile
     * @param bucketName
     * @param filePath
     * @return
     * @throws Exception
     */
    public MultipartFile downloadMultiPartFile(String bucketName, String objectName) throws Exception {

        log.info("-----bucket-----------"+bucketName);
        log.info("-----objectName-----------"+objectName);
        MultipartFile multipartFile =null;

        // 使用MinIO客户端从指定桶中获取文件
        try (var stream = getMiniClient().getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build())) {

            // 将InputStream转换为MultipartFile
            multipartFile = new MockMultipartFile(
                    "file",
                    objectName.substring(objectName.lastIndexOf("/") + 1),
                    "application/octet-stream",
                    stream);

            //return multipartFile;
        } catch (Exception e) {
            log.error("Exception------------------"+e.getMessage());

        }

        return multipartFile;

    }

    /**
     * 删除文件夹
     * @param bucketName 桶名称
     * @param folderPrefix 要删除的文件夹名称
     * @throws Exception
     */
    public void deleteFolder(String bucketName, String folderPrefix) throws Exception {

        Iterable<Result<Item>> results = getMiniClient().listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(folderPrefix).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = result.get();
            String objectName = item.objectName();
            getMiniClient().removeObject(
                    RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            System.out.println("Deleted: " + objectName);
        }
    }


    /**
     * 上传文件到MinIO
     *
     * @param file 要上传的文件
     * @param bucketName 在MinIO中存储的文件名
     * @return 存储在MinIO中的文件URL
     */
    public String uploadMultiFileToMinIO(MultipartFile file, String bucketName) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        //将文件上传到Minio中
        log.info("======bucketName======"+bucketName);
        log.info("======file.getContentType()======"+file.getContentType());
        log.info("======file.getInputStream()======"+file.getInputStream());
        // 检查存储桶是否已经存在
        boolean isExist = getMiniClient().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (isExist) {
            System.out.println("Bucket already exists.");
        } else {
            getMiniClient().makeBucket(
                    MakeBucketArgs.builder()
                         .bucket(bucketName)
                         .build());
        }
        // 生成唯一的文件名
        String objectName =new SimpleDateFormat("yyyy-MM-dd").format(new Date()) +"/"+
                UUID.randomUUID().toString() + "_" + file.getOriginalFilename();

        try (InputStream stream = file.getInputStream()) {
            // 上传文件到MinIO
            getMiniClient().putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(stream, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );
        } catch (IOException e) {
            throw new IOException("Failed to upload the file.", e);
        }
        String resUrl =   objectName;
        log.info("======文件路径======"+resUrl);
        return resUrl;
    }

    /***
     * 上传本地文件到MINIO
     * @param filePath
     * @param bucketName
     * @return
     * @throws IOException
     * @throws ServerException
     * @throws InsufficientDataException
     * @throws ErrorResponseException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws InvalidResponseException
     * @throws XmlParserException
     * @throws InternalException
     */
    public String uploadFileToMinIO(String filePath, String bucketName, String prefix) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        //将文件上传到Minio中
        log.info("======bucketName======" + bucketName);
        String resUrl ="";
        log.info("======file.getAbsolutePath()======" + filePath);
        // 检查存储桶是否已经存在
        boolean isExist = getMiniClient().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        log.info("==========isExist==========="+isExist);
        if (isExist) {
            System.out.println("Bucket already exists.");
        } else {
            getMiniClient().makeBucket(
                    MakeBucketArgs.builder()
                            .bucket(bucketName)
                            .build());
        }
        File originalFile=null;
        try {
            filePath=baseConfig.getUploadDir()+"/"+filePath;
            originalFile = new File(filePath);
            log.info("==========originalFile==========="+originalFile+"====originalFile.exists()======"+originalFile.exists());
            log.info("======originalFile.getName()======"+originalFile.getName());
            if(originalFile.exists()) {
                // 生成唯一的文件名
                String objectName = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/" +
                        UUID.randomUUID().toString() + "_" + originalFile.getName();
                if(!prefix.trim().equals(""))
                objectName=prefix+"/"+objectName;
                log.info("======file.objectName()======" + objectName);
                // 使用文件输入流上传文件
                try (InputStream stream = new FileInputStream(filePath)) {
                    // 创建PutObjectArgs对象
                    PutObjectArgs objectArgs = PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(stream, stream.available(), -1)
                            .contentType("text/plain")
                            .build();

                    // 调用putObject上传文件
                    getMiniClient().putObject(objectArgs);
                    System.out.println("文件上传成功！");


                    resUrl = objectName;
                    log.info("======文件路径======" + resUrl);
                    //return resUrl;
                }
            }
        }
        catch(Exception ex){
            log.error("exception--------------------------"+ex.getMessage());
        }
        finally {
            if(originalFile!=null)
                originalFile.delete();
        }
        return resUrl;
    }

    /**
     * 实现上传图片到Minio
     */
    public String upload(String bucketName, MultipartFile file) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        log.info("======bucketName======"+bucketName);
        log.info("======file======"+file.getSize());
        log.info("======file.getContentType()======"+file.getContentType());
        log.info("======文件桶名称======"+kmsfile);
        log.info("======accessKey======"+accessKey);
        log.info("======minioUrl======"+minioUrl);

        //将文件上传到Minio中
        // 检查存储桶是否已经存在
        boolean isExist = getMiniClient().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        log.info("======isExist======"+isExist);
        if (isExist) {
            System.out.println("Bucket already exists.");
        } else {
            // 创建一个名为 bucketName 的存储桶，用于存储照片的zip文件。
            getMiniClient().makeBucket(
                    MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            // 设置存储桶策略为公开。
            String sb = "{\"Version\":\"2012-10-17\"," +
                    "\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":" +
                    "{\"AWS\":[\"*\"]},\"Action\":[\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"," +
                    "\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::" + bucketName +
                    "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\"],\"Resource\":[\"arn:aws:s3:::" +
                    bucketName +
                    "/*\"]}]}";
            getMiniClient().setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .config(sb)
                            .build());
        }
        // 避免文件覆盖 生成日期文件夹格式+文件 ：年/月/日 + 文件名
        String originalFilename = file.getOriginalFilename(); //获取原始文件名
        String FileType = file.getContentType(); //获取原始文件名
        log.info("======originalFilename======"+originalFilename);
//        log.info("======originalFilenames======"+originalFilename.substring(originalFilename.lastIndexOf(".")));
        //拼接生成新的UUID形式的文件名
        String objectName = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) +"/"+
                UUID.randomUUID().toString().replaceAll("-", "")
                +"."+ FileType.substring(FileType.lastIndexOf("/")+1);
        log.info("======objectName======"+objectName);
        //2.通过Minio对象将图片上传到minio桶中
        // 使用putObject上传一个文件到存储桶中。
        PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName)
                .bucket(bucketName)
                .contentType(file.getContentType())
                .stream(file.getInputStream(), file.getSize(), -1).build();
        getMiniClient().putObject(objectArgs);
        log.info("======objectName2======"+objectName);
        //组装桶中文件的访问url
        String resUrl = minioPicture + "/" + bucketName + "/" + objectName;
        log.info("======resUrl======"+resUrl);
        return resUrl;
    }


    /**
     * 上传文件夹
     * @param bucketName 桶的名称
     * @param folderPath 本地文件路径
     * @param destPrefix 保存到miniio存储的文件夹名称,同本地根文件夹同名
     * @throws Exception
     */
    public void uploadFolder(String bucketName, String folderPath, String destPrefix) throws Exception {
        //验证桶是否存在,不存在则创建
        createBucket(bucketName);


        Path basePath = Paths.get(folderPath);

        log.info("本地文件路径:"+basePath);
        log.info("上传路径:"+destPrefix);


        Files.walkFileTree(basePath, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                try {
                    // 计算从文件夹路径到文件的相对路径
                    Path relativePath = basePath.relativize(file);
                    String objectName = destPrefix + File.separator + relativePath.toString();

                    // 正确处理MinIO对象名称的路径分隔符
                    // todo 有可能有系统不兼容风险
                    objectName = objectName.replace(File.separator, "/");

                    // 确定内容类型或使用默认值
                    String contentType = Files.probeContentType(file);
                    if (contentType == null) {
                        contentType = "application/octet-stream"; // Default content type
                    }

                    try (InputStream stream = Files.newInputStream(file)){
                        getMiniClient().putObject(
                                PutObjectArgs.builder().bucket(bucketName).object(objectName)
                                        .stream(stream, attrs.size(), -1)
                                        .contentType(contentType)
                                        .build()
                        );
                    }catch (Exception e){
                        e.printStackTrace();
                        return FileVisitResult.TERMINATE;
                    }
                    // 上传这个文件


                    System.out.println("Uploaded: " + file + " as " + objectName);

                } catch (Exception e) {
                    e.printStackTrace();
                    return FileVisitResult.TERMINATE;
                }
                return FileVisitResult.CONTINUE;
            }
        });
    }


    /**
     * 下载文件夹
     * @param bucketName 桶名称
     * @param prefix 需要下载的路径名 例-"testSecurity"
     * @param localDirPath 下载到本地的路径 例-"D:\\desktop\\miniioTest\\testSecurity"
     * @throws Exception
     */
    public void downloadFolder(String bucketName, String prefix, String localDirPath) throws Exception {

        Iterable<Result<Item>> results = getMiniClient().listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = result.get();
            String objectName = item.objectName();
            Path localFilePath = Paths.get(localDirPath, objectName.replace(prefix, ""));

            // Ensure the directories in the path are created
            Files.createDirectories(localFilePath.getParent());

            // Download the file
            InputStream stream = getMiniClient().getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            Files.copy(stream, localFilePath, StandardCopyOption.REPLACE_EXISTING);
            stream.close();
            System.out.println("Downloaded: " + objectName + " to " + localFilePath);
        }
    }



}
